package com.homedo.odin.solution.config.aspect;

import com.homedo.core.common.base.dto.Meta;
import com.homedo.core.common.base.dto.ResultData;
import com.homedo.core.common.constant.Global;
import com.homedo.odin.solution.annotation.UserActionLogRecorder;
import com.homedo.odin.solution.model.base.BaseResponse;
import com.homedo.odin.solution.model.entity.UserActionLog;
import com.homedo.odin.solution.service.UserActionLogServiceImpl;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 用户行为日志切面 记录的条件有以下几点： 1.只切 Controller 方法 2.返回结果必须是ResultData类型 3.返回码是200 4.返回单个对象要继承BaseResponse
 *
 * @author chenlei
 * @date 2018/5/28
 */
@Aspect
@Component
public class UserActionLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(UserActionLogAspect.class);

    @Autowired
    private UserActionLogServiceImpl userActionLogService;

    @Pointcut("execution(public * com.homedo.odin.solution.controller.*.*.*(..))")
    public void controller() {
    }

    /**
     * 获取方法返回结果
     */
    @Around("controller() && @annotation(userActionLogRecorder)")
    public Object arround(ProceedingJoinPoint pjp, UserActionLogRecorder userActionLogRecorder) {
        logger.info("用户行为日志记录开始.....");
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                    .getRequest();
            Object o = pjp.proceed();
            if (o instanceof ResultData) {
                ResultData resultData = (ResultData) o;
                if (null != resultData && resultData.getCode() == Meta.CODE_200.getCode()) {
                    // 当返回码是200时才处理
                    Object data = resultData.getData();
                    if (data instanceof List) {
                        // 返回的是列表数据，需要循环处理
                        List list = (List) data;
                        list.forEach(item -> recordLog(item, userActionLogRecorder, request));
                    } else if (data instanceof BaseResponse) {
                        // 返回的是单个对象
                        recordLog(data, userActionLogRecorder, request);
                    }
                }
            } else {
                logger.warn("返回值为非ResultData类型，无法记录");
            }
            logger.info("用户行为日志记录结束...");
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过字段名从对象或对象的父类中得到字段的值（调用字典的get方法）
     *
     * @param object 对象实例
     * @param fieldName 字段名
     * @return 字段对应的值
     */
    private Object getFieldValueByFieldName(String fieldName, Object object) {
        if (object == null) {
            return null;
        }
        if (StringUtils.isBlank(fieldName)) {
            return null;
        }
        Field field = null;
        Class<?> clazz = object.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                //获得get方法
                Method getMethod = pd.getReadMethod();
                //执行get方法返回一个Object
                return getMethod.invoke(object);
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }

        return null;
    }

    private void recordLog(Object item, UserActionLogRecorder userActionLogRecorder, HttpServletRequest request) {
        Object id = getFieldValueByFieldName(userActionLogRecorder.id(), item);
        if (null == id) {
            logger.warn("获取到的 ID 值为空。");
            return;
        }

        Object name = getFieldValueByFieldName(userActionLogRecorder.name(), item);
        if (null == name) {
            logger.warn("获取到的 name 值为空。");
        }
//        request.getParameter("userId");

        UserActionLog log = new UserActionLog();
        log.setBusinessType(userActionLogRecorder.bizType().getDbValue());
        log.setText(name == null ? null : name.toString());
        log.setBusinessId(Long.valueOf(id.toString()));
        log.setIp(request.getRemoteAddr());
        log.setReferer(request.getHeader(Global.REQ_REFERER));
        userActionLogService.recordLog(log);

        logger.debug("记录UserActionLog：" + log);
    }
}
